#include "stdafx.h"
#include "PersistenceController.h"

#include "../db/Item.h"
#include "../db/ItemType.h"
#include "../db/ItemProperty.h"
#include "../db/ItemCategory.h"
#include "../db/PropertyTemplate.h"
#include "../db/Town.h"
#include "../db/Alchemy.h"
#include "Item.h"
#include "ItemType.h"
#include "ItemCategory.h"
#include "Property.h"
#include "PropertyTemplate.h"
#include "Town.h"
#include "Alchemy.h"
#include "Model.h"
#include "EventManagerSngl.h"
#include "EventCodes.h"
#include "Pred.h"
#include <utility/EventType.h>
#include <utility/EventManagerMarco.h>
#include <sstream>

#ifdef WIN32
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif

#define HANDLE_UI_EVENT(name) EVENT_CODE_FILTER(UEC::##name, On##name)
#define HANDLE_P_EVENT(name) EVENT_CODE_FILTER(PEC::##name, On##name)

namespace serika
{
	namespace dq
	{
		PersistenceController::PersistenceController()
			: mHasReadItems(false)
		{
			EventManager::AttachUiEventHandler(this);
			EventManager::AttachHandler(EventType::Persistence, this);
		}

		PersistenceController::~PersistenceController()
		{
			EventManager::DetachUiEventHandler(this);
			EventManager::DetachHandler(EventType::Persistence, this);
		}

		void PersistenceController::Handle(const ut::Event& e)
		{
			switch (e.GetType())
			{
			case ut::EventType::UI:
				this->OnUiEvent(e);
				break;

			case EventType::Persistence:
				this->OnPersistenceEvent(e);
				break;
			}
		}

		void PersistenceController::OnUiEvent(const ut::Event& e)
		{
			switch (e.GetCode())
			{
				HANDLE_UI_EVENT(ItemTabInit);
				HANDLE_UI_EVENT(RequestNewItem);
				HANDLE_UI_EVENT(RequestPropertyTemplates);
				HANDLE_UI_EVENT(RequestItemCategories);
				HANDLE_UI_EVENT(RequestItemList);
				HANDLE_UI_EVENT(CreateItem);
				HANDLE_UI_EVENT(SaveItem);
				HANDLE_UI_EVENT(RequestTownList);
				HANDLE_UI_EVENT(RequestItemProperties);
				HANDLE_UI_EVENT(RequestAlchemyList);
			}
		}

		void PersistenceController::OnRequestNewItem(const ut::Event& e)
		{
			Item* item = this->NewItem();
			Model::SetUnconfirm(item);
			ut::Event* itemEvent = ut::Event::NewEvent(ut::EventType::Logic, LEC::Item);
			itemEvent->SetUserData(item);
			EventManager::SendEvent(itemEvent);
		}

		void PersistenceController::OnRequestPropertyTemplates(const ut::Event& e)
		{
			const PropertyTemplates& tmpls = this->GetPropertyTemplates(e.GetSource());
			ut::Event* tmplEvent = ut::Event::NewEvent(ut::EventType::Logic, LEC::PropertyTemplates);
			tmplEvent->SetUserData(const_cast<PropertyTemplates*>(&tmpls));
			EventManager::SendEvent(tmplEvent);
		}

		void PersistenceController::OnRequestItemCategories(const ut::Event& e)
		{
			const ItemCategories& categories = this->GetItemCategories(e.GetSource());
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::ItemCategories);
			evn->SetUserData(const_cast<ItemCategories*>(&categories));
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestItemList(const ut::Event& e)
		{
			Items items;
			this->GetItems(e.GetSource(), items);
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::ItemList);
			evn->SetUserData(&items);
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestItemProperties(const ut::Event& e)
		{
			this->GenItemProperties(e.GetUserData<Item>());
		}

		void PersistenceController::OnItemTabInit(const ut::Event& e)
		{
			std::vector<db::ItemType*> dbItemTypes;
			this->GetAllOneType(dbItemTypes);

			std::vector<ItemType> itemTypes;
			for (std::vector<db::ItemType*>::iterator itr = dbItemTypes.begin();
				itr != dbItemTypes.end(); ++itr)
			{
				db::ItemType* dbItemType = *itr;
				ItemType itemType;
				itemType.id = dbItemType->GetRawValue<int>("id");
				itemType.name = dbItemType->GetRawValue<ut::String>("name");
				itemTypes.push_back(itemType);
				delete dbItemType;
			}

			ut::Event* itemTypeEvent = ut::Event::NewEvent(ut::EventType::Logic, LEC::ItemType);
			itemTypeEvent->SetUserData(&itemTypes);
			EventManager::SendEvent(itemTypeEvent);
		}

		void PersistenceController::OnCreateItem(const ut::Event& e)
		{
			Item* item = e.GetUserData<Item>();
			Model::AddNewItem(item);
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::CreateItem);
			evn->SetUserData(item);
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnSaveItem(const ut::Event& e)
		{
			Items items;
			Model::PullNewItems(items);
			this->Save(items);
			Model::AddItems(items);

			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::ItemList);
			evn->SetUserData(&items);
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestTownList(const ut::Event& e)
		{
			const Towns& towns = this->GetTowns();
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::TownList);
			evn->SetUserData(const_cast<Towns*>(&towns));
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestAlchemyList(const ut::Event& e)
		{
			const Alchemies& alchemies = this->GetAlchemies();
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::AlchemyList);
			evn->SetUserData(const_cast<Alchemies*>(&alchemies));
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnPersistenceEvent(const ut::Event& e)
		{
			switch (e.GetCode())
			{
				HANDLE_P_EVENT(RequestProduct);
				HANDLE_P_EVENT(RequestMaterial1);
				HANDLE_P_EVENT(RequestMaterial2);
				HANDLE_P_EVENT(RequestMaterial3);
			}
		}

		void PersistenceController::OnRequestProduct(const ut::Event& e)
		{
			Item* item= this->GetItem(e.GetSource());
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::ProductItem);
			evn->SetUserData(item);
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestMaterial1(const ut::Event& e)
		{
			Item* item= this->GetItem(e.GetSource());
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::Material1Item);
			evn->SetUserData(item);
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestMaterial2(const ut::Event& e)
		{
			Item* item= this->GetItem(e.GetSource());
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::Material2Item);
			evn->SetUserData(item);
			EventManager::SendEvent(evn);
		}

		void PersistenceController::OnRequestMaterial3(const ut::Event& e)
		{
			Item* item= this->GetItem(e.GetSource());
			ut::Event* evn = ut::Event::NewEvent(ut::EventType::Logic, LEC::Material3Item);
			evn->SetUserData(item);
			EventManager::SendEvent(evn);
		}

		inline Item* PersistenceController::NewItem()
		{
			Item* item = Item::NewItem(0, 0, 0, _T(""), _T(""));
			//Model::AddItem(item);
			return item;
		}

		void PersistenceController::Search(const ut::String& name, Items& items)
		{
			const Items& modes = Model::GetItems();

			if (modes.empty())
			{
				return;
			}

			// not implement
			ASSERT(false);
		}

		void PersistenceController::GenItemProperties(const Items& items)
		{
			for (Items::const_iterator itr = items.begin(); itr != items.end(); ++itr)
			{
				this->GenItemProperties(*itr);
			}
		}

		void PersistenceController::GenItemProperties(Item* item)
		{
			ut::String ids = item->GetPropertyIds();
			if (!ids.empty())
			{
				Properties properties;
				this->GetAllTwoType<Property, db::ItemProperty>(properties, ids);
				item->AddProperty(properties);
			}
		}

		inline const PropertyTemplates& PersistenceController::GetPropertyTemplates(int itemType)
		{
			const PropertyTemplates& tmpls = Model::GetPropertyTemplates(itemType);
			if (tmpls.empty())
			{
				std::stringstream conditions;
				conditions << "itpm.item_type=" << itemType;
				std::string joins = "as p inner join item_type_property_mappings as itpm on itpm.property_id=p.id";
				std::vector<db::PropertyTemplate*> dbs;
				db::PropertyTemplate::FindAll(dbs, conditions.str(), 0, 0, "p.*", joins);

				PropertyTemplates t;
				this->ToTransient(t, dbs);
				Model::AddPropertyTemplates(itemType, t);
			}

			return tmpls;
		}

		inline const ItemCategories& PersistenceController::GetItemCategories(int itemType)
		{
			const ItemCategories& categories = Model::GetItemCategories(itemType);
			if (categories.empty())
			{
				std::stringstream conditions;
				conditions << "item_type=" << itemType;
				std::vector<db::ItemCategory*> dbs;
				db::ItemCategory::FindAll(dbs, conditions.str());

				ItemCategories c;
				this->ToTransient(c, dbs);
				Model::AddItemCategories(itemType, c);
			}

			return categories;
		}

		inline void PersistenceController::GetItems(int itemType, Items& items)
		{
			const Items& itms = this->GetAllItems();

			if (0 == itemType)
			{
				items = itms;
			}
			else
			{
				std::remove_copy_if(itms.begin(), itms.end(), std::back_inserter(items),
					std::not1(ItemTypeFilterPred(itemType)));
			}
		}

		inline const Items& PersistenceController::GetAllItems()
		{
			if (!mHasReadItems)
			{
				Items its;
				this->GetAllTwoType<Item, db::Item>(its);
				this->GenItemProperties(its);
				Model::AddItems(its);
				mHasReadItems = true;
			}

			return Model::GetItems();
		}

		inline Item* PersistenceController::GetItem(int id)
		{
			const Items& items = this->GetAllItems();
			Items::const_iterator itr = std::lower_bound(items.begin(), items.end(), id, LessItemIdPred());
			Item* item = *itr;
			return id == item->GetId() ? item : 0;
		}

		inline const Towns& PersistenceController::GetTowns()
		{
			const Towns& towns = Model::GetTowns();

			if (towns.empty())
			{
				Towns ts;
				this->GetAllTwoType<Town, db::Town>(ts);
				Model::AddTowns(ts);
			}

			return towns;
		}

		inline const Alchemies& PersistenceController::GetAlchemies()
		{
			const Alchemies& alchemies = Model::GetAlchemies();

			if (alchemies.empty())
			{
				Alchemies as;
				this->GetAllTwoType<Alchemy, db::Alchemy>(as);
				Model::AddAlchemies(as);
			}

			return alchemies;
		}

		template<class Type, class DbType>
		void PersistenceController::GetAllTwoType(std::vector<Type*>& types, const ut::String& ids/* = ""*/)
		{
			std::vector<DbType*> dbs;
			this->GetAllOneType(dbs, ids);
			this->ToTransient(types, dbs);
		}

		template<class DbType>
		void PersistenceController::GetAllOneType(std::vector<DbType*>& dbs, const ut::String& ids /* = */ )
		{
			if (ids.empty())
			{
				DbType::FindAll(dbs);
			}
			else
			{
#ifdef _UNICODE
				std::string conditions = "id in (" + ut::StringHelper::ToString(ids) + ")";
#else
				std::string conditions = "id in (" + ids + ")";
#endif
				DbType::FindAll(dbs, conditions);
			}
		}

		template<class Type, class DbType>
		void PersistenceController::ToTransient(std::vector<Type*>& types, const std::vector<DbType*>& dbs)
		{
			for (std::vector<DbType*>::const_iterator itr = dbs.begin(); itr != dbs.end(); ++itr)
			{
				types.push_back(Type::FromDb(*itr));
			}
		}

		template<class Type>
		void PersistenceController::Save(const std::vector<Type*>& records)
		{
			for (std::vector<Type*>::const_iterator iter = records.begin();
				iter != records.end(); ++iter)
			{
				(*iter)->Save();
			}
		}

		template<class Type>
		void PersistenceController::Delete(const std::vector<Type*>& records)
		{
			for (std::vector<Type*>::const_iterator iter = records.begin();
				iter != records.end(); ++iter)
			{
				(*iter)->Delete();
			}
		}
	}
}
